/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2021. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file eep_flash_drv.c
 *
 * @brief This file provides FLASH integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_eflash_reg.h"
#include "eep_drv.h"

/* ============================================  Define  ============================================ */
/* FLASH command timeout */
#define AC7803x_FLASH_CMD_TIMEOUT            (2UL)
/*!< FLASH controler unlock key */
#define AC7803x_FLASH_UNLOCK_KEY1            (0xac7803UL)
#define AC7803x_FLASH_UNLOCK_KEY2            (0x01234567UL)
/*!< Based address of P-flash user area */
#define AC7803x_PFLASH_BASE_ADDRESS          (0x08000000UL)
/*!< Total size for P-flash user area */
#define AC7803x_PFLASH_SIZE                  (0x00040000UL)
/*!< P-flash page size */
#define AC7803x_PFLASH_PAGE_SIZE             (0x00000400UL)
/*!< End address of P-flash user area */
#define AC7803x_PFLASH_END_ADDRESS           (AC7803x_PFLASH_BASE_ADDRESS + AC7803x_PFLASH_SIZE)
/*!< P-flash page total page amount */
#define AC7803x_PFLASH_PAGE_AMOUNT           (AC7803x_PFLASH_SIZE / AC7803x_PFLASH_PAGE_SIZE)
/*!< Based address of D-flash user area */
#define AC7803x_DFLASH_BASE_ADDRESS          (0x08040000UL)
/*!< Total size for D-flash user area */
#define AC7803x_DFLASH_SIZE                  (0x00040000UL)
/*!< D-flash page size */
#define AC7803x_DFLASH_PAGE_SIZE             (0x00000400UL)
/*!< End address of D-flash user area */
#define AC7803x_DFLASH_END_ADDRESS           (AC7803x_DFLASH_BASE_ADDRESS + AC7803x_DFLASH_SIZE)
/*!< D-flash page total page amount */
#define AC7803x_DFLASH_PAGE_AMOUNT           (AC7803x_DFLASH_SIZE / AC7803x_DFLASH_PAGE_SIZE)
/*!< Flash program unit size */
#define AC7803x_FLASH_WRITE_UNIT_SIZE        (4UL)


/* ====================================  Functions declaration  ===================================== */
extern Eep_CallbackType g_eepCallback;
/* =====================================  Functions definition  ===================================== */

/*!
 * @brief Perform start command sequence on FLASH.
 *
 * @param[in] config: The FLASH configuration structure pointer
 * @return operation status
 */
static Eep_StatusType Flash_StartCmdSequence(void)
{
    Eep_StatusType  Ret = EEP_SUCCESS;

    /* trigger command execute */
    EFLASH_TrigCtrlCmdReg();
    /* Wait till busy bit is cleared */
    while (EFLASH_GetCmdBusyStatus())
    {
        if (0U != g_eepCallback)
        {
            g_eepCallback();
        }
    }

    /* Check command execute error status */
    if (EFLASH_GetStatusReg())
    {
        Ret = EEP_STATU_ERROR;
    }

    return Ret;
}

/*!
 * @brief Unlock the FLASH register.
 *
 * @param[in] none
 * @return operation status
 *        - 0U: Operation was successful
 *        - 3U: Operation timeout was occurred
 */
Eep_StatusType Eep_Flash_UnlockCtrl(void)
{
    Eep_StatusType Ret = EEP_SUCCESS;
    uint8_t CtrlLockBit = 0U;
    uint32_t TimeoutCount = 0U;

    CtrlLockBit = EFLASH_GetCtrlLockBitReg();
    while (1U == CtrlLockBit)
    {
        EFLASH->UKR = AC7803x_FLASH_UNLOCK_KEY1;
        EFLASH->UKR = AC7803x_FLASH_UNLOCK_KEY2;

        if (100U < TimeoutCount)
        {
            Ret = EEP_TIMEOUT;
            break;
        }
        CtrlLockBit = EFLASH_GetCtrlLockBitReg();
        TimeoutCount++;
    }
    /* enable LVD detect */
    EFLASH->LVD = 0x10000000U;
    return Ret;
}

/*!
 * @brief Lock the FLASH register.
 *
 * @param[in] none
 * @return none
 */
void Eep_Flash_LockCtrl(void)
{
    EFLASH_LockCtrlReg();
}

/*!
 * @brief EEP group verify.
 *
 * @param[in] addr: Start address for verify operation
 * @param[in] size: Verify size in 64-bit
 * @return operation status
 */
Eep_StatusType Eep_Flash_VerifyGroup(uint32_t Addr, uint32_t Size)
{
    Eep_StatusType Ret = EEP_SUCCESS;

    if ((AC7803x_PFLASH_BASE_ADDRESS <= Addr) && (AC7803x_DFLASH_END_ADDRESS > Addr))
    {
        /* Wait for last operation to be completed */
        if (EFLASH_GetCmdBusyStatus())
        {
            Ret = EEP_BUSY;
        }
        else
        {
            EFLASH_CLEAR_STAT_ERROR_BITS();
            /* Configure start address */
            EFLASH_SetStartAddressReg(Addr);

            /* Configure command and CMD_ST goes back to 0 */
            EFLASH_SetCommandReg((uint32_t)0x7U);
            /* Configure length (unit is 32-bit)*/
            EFLASH_SetLengthReg(Size);

            /* Trigger to start */
            Ret = Flash_StartCmdSequence();
        }
    }
    else
    {
        Ret = EEP_PARA_ERROR;
    }

    return Ret;
}

/*!
 * @brief EEP erase GROUP.
 *
 * @param[in] addr: Erase sector start address
 * @param[in] size: Erase size in bytes
 * @return operation status
 */

Eep_StatusType Eep_Flash_EraseGroup(uint32_t Addr, uint32_t Size)
{
    Eep_StatusType Ret = EEP_SUCCESS;
    uint32_t SectorSize = 0;
    uint32_t Temp = 0;
    uint32_t TempSize = Size;

    if ((AC7803x_PFLASH_BASE_ADDRESS <= Addr) && (AC7803x_DFLASH_END_ADDRESS > Addr))
    {
        Temp = AC7803x_DFLASH_BASE_ADDRESS;

        if ((Addr >= Temp) && (Addr < (Temp + AC7803x_DFLASH_SIZE)))
        {
            SectorSize = (uint32_t)AC7803x_DFLASH_PAGE_SIZE;
        }
        else
        {
            Temp = AC7803x_PFLASH_BASE_ADDRESS;
            if (Addr < (Temp + AC7803x_PFLASH_SIZE))
            {
                SectorSize = (uint32_t)AC7803x_PFLASH_PAGE_SIZE;
            }
            else
            {
                Ret = EEP_PARA_ERROR;
                TempSize = 0U;
                SectorSize = 0U;
            }
        }

        /* Check if the tempSize is alignment with flash sector size */
        if (0U != (TempSize & (SectorSize - 1U)))
        {
            Ret = EEP_PARA_ERROR;
        }

        while ((TempSize > 0U) && (EEP_SUCCESS == Ret))
        {
            /* Wait for last operation to be completed */
            if (EFLASH_GetCmdBusyStatus())
            {
                Ret = EEP_BUSY;
            }
            else
            {
                EFLASH_CLEAR_STAT_ERROR_BITS();
                /* Configure start address */
                EFLASH_SetStartAddressReg(Addr);
                /* Configure command */
                EFLASH_SetCommandReg((uint32_t)0x1U);
                /* Trigger to start */
                Ret = Flash_StartCmdSequence();
                /* Update size and address */
                TempSize -= SectorSize;
                Addr += SectorSize;
            }
        }
    }
    else
    {
        Ret = EEP_PARA_ERROR;
    }

    return Ret;
}

/*!
 * @brief FLASH program.
 *
 * @param[in] addr: Program start address
 * @param[in] size: Program size in byte
 * @param[in] data: Pointer of data source address from which will be programmed
 * @return operation status
 *        - 0U: Operation was successful.
 *        - 1U: Operation was failure.
 *        - 4U: Operation was unsupported.
 *        - 2U: Operation was busy.
 */
Eep_StatusType Eep_Flash_Program(uint32_t Addr, uint32_t Size, const uint8_t *Data)
{
    Eep_StatusType Ret = EEP_SUCCESS;
    uint32_t Data1;

    if (0U != (Size & (AC7803x_FLASH_WRITE_UNIT_SIZE - 1U)))
        return EEP_PARA_ERROR;

    if((AC7803x_PFLASH_BASE_ADDRESS <= Addr) && (AC7803x_DFLASH_END_ADDRESS > Addr))
    {
        /* Wait for last operation to be completed */
        if (EFLASH_GetCmdBusyStatus())
        {
            Ret = EEP_BUSY;
        }
        else
        {
            EFLASH_CLEAR_STAT_ERROR_BITS();
            /* Configure start address */
            EFLASH_SetStartAddressReg(Addr);
            /* Configure length (unit is 32-bit)*/
            EFLASH_SetLengthReg(1);
            /* Configure command */
            EFLASH_SetCommandReg((uint32_t)0x5U);

            /* Trigger to start */
            EFLASH_TrigCtrlCmdReg();

            /* Check error */
            if (!(EFLASH_GetStatusReg() & EFLASH_CSR_PGMERR_Msk))
            {
                /*program data */
                while ((Size > 0U) && (EEP_SUCCESS == Ret))
                {
                    if (EFLASH_GetDataBufferReg())
                    {
                        Data1 = (Data[0] | (Data[1] << 8U) | (Data[2] << 16U) | (Data[3] << 24U));
                        EFLASH_SetDataReg(Data1);
                        /* Update program address for next operation */
                        Addr += AC7803x_FLASH_WRITE_UNIT_SIZE;
                        /* Update size for next operation */
                        Size -= AC7803x_FLASH_WRITE_UNIT_SIZE;

                        /* Increase the data source pointer by 8 */
                        Data += AC7803x_FLASH_WRITE_UNIT_SIZE;
                    }
                }
                /* Check whether the command is finished */
                while (EFLASH_GetCmdBusyStatus());
                if (EFLASH_GetStatusReg())
                {
                    Ret = EEP_STATU_ERROR;
                }
            }
            else
            {
                Ret = EEP_STATU_ERROR;
            }
        }
    }
    else
    {
        Ret = EEP_PARA_ERROR;
    }

    return Ret;
}

/*!
 * @brief Check FLASH current busy status
 *
 * @param[in] none
 * @return EEP current status busy or idle
 */
Eep_StatusType Eep_Flash_CheckBusy(void)
{
    if (EFLASH_GetCmdBusyStatus())
    {
        return EEP_BUSY;
    }
    else
    {
        return EEP_SUCCESS;
    }
}

/*!
 * @brief CSE size determines whether to init eep.
 *
 * @param[in] ram base addr
 * @param[in] flash base addr
 * @return operation status
 */
Eep_StatusType Eep_Flash_PreInitCheck(uint32_t RamAddr, uint32_t FlashAddr)
{
    Eep_StatusType Ret = EEP_SUCCESS;

    return Ret;
}

/* =============================================  EOF  ============================================== */
